#ifndef __MY_SERVER__
#define __MY_SERVVER__
#include "httplib.h"
#include "DataManager.hpp"
extern hhbcloud::DataManager* data;
namespace hhbcloud
{
    class Service
    {
    public:
        Service()
        {
            Config* config = Config::GetInstance();
            // 从配置文件中读取服务器端口，服务器ip，下载前缀
            _server_port = config->GetServerPort();
            _server_ip = config->GetServerIp();
            _download_prefix = config->GetDownloadPrefix();

            // 若文件此时没有指定备份目录，则需要创建文件目录
            std::string back_dir = config->GetBackDir();
            FileUtil fu(back_dir);
            fu.CreateDirectory(); // createDirectory函数内部对文件存在做了判断，如存在则不创建
        }

        bool RunMoudle() // 运行模块
        {
            // post请求上传文件， 并且业务处理函数Upload的参数必须是Request 和 Response,所以必须要静态，否则会有隐含的this指针参数
            _server.Post("/upload", Upload);

            _server.Get("/listshow", ListShow);
            _server.Get("/", ListShow);

            std::string download = _download_prefix + "(.*)"; // .*正则表达式表示匹配任意字符，任意次
            _server.Get(download, DownLoad);

            // listen启动服务器，并指定监听的主机和端口
            _server.listen(_server_ip.c_str(), _server_port);

            return true;

        }
    private:
        static void Upload(const httplib::Request& req, httplib::Response& resp)
        {
            auto ret = req.has_file("file"); // 判断客户端上传有没有这个字段
            if (ret == false)
            {
                // 没有这个字段，服务器无法处理，设置状态码为400，并返回
                resp.status = 400;
                return;
            }

            std::cout << "has_file && upload" << std::endl;

            // 有字段，获取文件内容
            auto file = req.get_file_value("file");
            // 在备份文件目录下创建文件，并写入数据
            std::string back_dir = Config::GetInstance()->GetBackDir();
            // 获取文件的实际存储路径 -- /backdir/filename 
            std::string realpath = back_dir + FileUtil(file.filename).FileName();
            FileUtil fu(realpath);
            // 向文件中写入数据 写入数据函数中ofstream在打开文件时，若文件不存在，会自动创建文件， 若没有指定目录，则需要自己创建
            fu.SetContent(file.content);

            // 组织备份信息
            Backinfo backinfo;
            backinfo.NewBackinfo(realpath);

            data->Insert(backinfo); // 将数据写入备份信息文件中

            return;
        }

        static std::string TimetoStr(time_t time)
        {
            return ctime(&time);
        }
        static void ListShow(const httplib::Request& req, httplib::Response& resp)
        {
            // 获取备份信息文件中的所有数据
            std::vector<Backinfo> array;
            data->GetAll(&array);

            // 使用备份信息组织html页面 
            std::stringstream ss;

            ss << "<html><meta charset = 'utf-8'><head><title>cloudBackup</title></head>";
            ss << "<body><h1>Download</h1><table>";
            for (auto& e : array)
            {
                // 构建每一行的数据 文件名称 文件最近访问时间 文件大小(k)
                ss << "<tr>";
                std::string filename = FileUtil(e._real_path).FileName();

                ss << "<td>" << "<a href = '" << e._url_path << "'>" << filename << "</a></td>";
                ss << "<td>" << TimetoStr(e._atime) << "</td>";
                ss << "<td>" << e._fsize / 1024 << " K" << "</td>";
                ss << "</tr>";
            }
            ss << "</table></body></html>";

            resp.body = ss.str();
            resp.status = 200;
            resp.set_header("Content-Type", "text/html");

            return;
        }

        // 由文件名，文件大小， 文件最后一次修改构成的ETag信息
        static std::string GetETag(const Backinfo& backinfo)
        {
            FileUtil fu(backinfo._real_path);
            std::string ETag = fu.FileName();
            ETag += "-";
            ETag += fu.FileSize();
            ETag += "-";
            ETag += fu.LastMTime();

            return ETag;
        }

        static void DownLoad(const httplib::Request& req, httplib::Response& resp)
        {
            // 通过url路径获取文件的备份信息
            // req.path就是文件的url路径
            Backinfo backinfo;
            data->GetOneByUrl(req.path, &backinfo);

            // 如果文件压缩标志是true，则需要先解压缩
            if (backinfo._pack_flag == true)
            {
                FileUtil fu(backinfo._pack_path);

                fu.Uncompress(backinfo._real_path); // 解压到真实路径
                fu.Remove(); // 删除压缩包
                // 更改文件压缩标志，并更改文件备份信息
                backinfo._pack_flag = false;
                data->Update(backinfo);
            }

            // 将解压后的文件的内容写到resp.body中
            FileUtil fu(backinfo._real_path);

            bool retrans = false; // 标记是否是断电续传
            std::string old_etag; // 下载请求时，已有的Etag值

            // 判断是否有if-Range， 若有则是断点续传，若没有则不是
            if (req.has_header("if-Range"))
            {
                old_etag = req.get_header_value("if-Range");

                if (old_etag == GetETag(backinfo))// 有if-Range，但是与当前的Etag不一致，则也需要正常下载
                {
                    retrans = true;
                }
            }

            if (retrans == false)
            {
                // 如果retrans == false ，需要断点续传，否则不需要
                fu.GetContent(&resp.body); // 读取文件内容，写入resp.body
                // 设置响应报头字段
                resp.set_header("Accept-Ranges", "bytes"); // 允许断点续传
                resp.set_header("ETag", GetETag(backinfo));
                // Content-Type 决定了浏览器如何处理数据，设置文件二进制流，常用于文件下载
                resp.set_header("Content-Type", "application/octet-stream");
                resp.status = 200;
                return;

            }
            else
            {
                // 此时已经是要断点续传了，按照Range字段中指定的区间，取出指定区间的数据进行下载
                // httplib库已经内置了，这里只需要取出数据即可
                fu.GetContent(&resp.body); // 读取文件内容，写入resp.body
                // 设置响应报头字段
                resp.set_header("Accept-Ranges", "bytes"); // 允许断点续传
                resp.set_header("ETag", GetETag(backinfo));
                // Content-Type 决定了浏览器如何处理数据，设置文件二进制流，常用于文件下载
                resp.set_header("Content-Type", "application/octet-stream");
                resp.status = 206;
                return;
            }
        }
    private:
        int _server_port; // 服务器开放端口号
        std::string _server_ip; // 服务器ip
        std::string _download_prefix; // 下载前缀
        httplib::Server _server; // 实例化一个服务器Server对象 ，自动调用构造函数初始化
    };
}





#endif